package cn.dansj.common.utils.transfer;

import cn.dansj.common.utils.enums.BooleanType;
import cn.dansj.common.utils.reflect.ClassUtils;
import oracle.sql.TIMESTAMP;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public abstract class Transformation {
    private static final Base64.Decoder decoder = Base64.getDecoder();
    private static final Base64.Encoder encoder = Base64.getEncoder();
    private static final boolean hasTimestampClass = ClassUtils.isPresent("oracle.sql.TIMESTAMP");
    public static final Supplier<String> emptyStringSupplier = () -> "";

    public static int intValue(BigDecimal decimal) {
        if (decimal == null) {
            return 0;
        } else {
            int scale = decimal.scale();
            return scale >= -100 && scale <= 100 ? decimal.intValue() : decimal.intValueExact();
        }
    }

    public static boolean castToBoolean(Object value) {
        if (Verification.checkNull(value)) return false;

        List<String> falseList = ArrayUtils.asList("false", "no", "0", "", "n", "null", "undefined", "unsupported");
        if (value instanceof String) {
            return !falseList.contains(value.toString().toLowerCase());
        } else if (value instanceof Boolean) {
            return (Boolean) value;
        } else if (value instanceof BigDecimal) {
            return intValue((BigDecimal) value) > 0;
        } else if (value instanceof Number) {
            return ((Number) value).intValue() > 0;
        } else if (value instanceof BooleanType) {
            return BooleanType.TRUE.equals(value);
        } else {
            return false;
        }
    }

    public static int transBooleanToInt(boolean b) {
        return b ? 1 : 0;
    }

    public static String timestampToTime(Object timestamp, String timeFormat) {
        long time = 0;
        try {
            if (timestamp instanceof String) {
                time = Long.parseLong(timestamp.toString());
            } else if (timestamp instanceof Long) {
                time = (long) timestamp;
            }
        } catch (NumberFormatException ignored) {
        }

        //将时间戳转换为时间
        Date date = new Date(time);

        //调整时间样式
        return GetTime.stringTime(timeFormat, date);
    }

    public static String timestampToTime(Object timestamp) {
        return timestampToTime(timestamp, "yyyy-MM-dd HH:mm:ss");
    }

    @SafeVarargs
    public static <T> T nvl(T... args) {
        for (T arg : args) {
            if (Verification.checkNotNull(arg)) {
                return arg;
            }
        }

        return args[args.length - 1];
    }

    public static String sqlLikeToLowCase(String col) {
        return "%" + col.toLowerCase() + "%";
    }

    /**
     * 首字母大写
     */
    public static String capitalize(String text) {
        return text.substring(0, 1).toUpperCase() + text.substring(1).toLowerCase();
    }

    public static boolean isNumeric(Object val) {
        if (val == null) return false;
        return Pattern.matches("[-+]?[0-9]*\\.?[0-9]+$", StringUtils.toString(val));
    }

    public static boolean isNumber(Object val) {
        if (val == null) return false;
        return Pattern.matches("\\d+", StringUtils.toString(val));
    }

    enum errorEnums {
        strToLong("too long for column '(.*?)'", "%s列文本超长,无法保存", true), duplicateEntry("Duplicate entry '(.*?)'", "重复值:%s,无法保存", true);
        private final String compile;
        private final String mean;
        private final boolean format;

        errorEnums(String compile, String mean, boolean format) {
            this.compile = compile;
            this.mean = mean;
            this.format = format;
        }

        public String getCompile() {
            return compile;
        }

        public String getMean() {
            return mean;
        }

        public boolean isFormat() {
            return format;
        }
    }

    public static String translateException(String e, String def) {
        if (e == null) {
            return def;
        }
        for (errorEnums en : errorEnums.values()) {
            Pattern pattern = Pattern.compile(en.getCompile());
            Matcher matcher = pattern.matcher(e);
            if (matcher.find() && matcher.group() != null) {
                if (en.isFormat()) {
                    return String.format(en.getMean(), matcher.group(1));
                } else {
                    return en.getMean();
                }
            }
        }
        return def;
    }

    public static String translateException(String e) {
        return translateException(e, e);
    }

    public static String translateException(Exception e, String def) {
        return translateException(e.getMessage(), def);
    }

    public static String translateException(Exception e) {
        return translateException(e.getMessage(), e.getMessage());
    }

    public static long parseLong(Object value, long def) {
        if (value == null) {
            return def;
        }

        if (value instanceof Number) {
            return ((Number) value).longValue();
        }

        try {
            return Long.parseLong(String.valueOf(value));
        } catch (Exception exception) {
            return def;
        }
    }

    public static long parseLong(Object s) {
        return parseLong(s, 0);
    }

    public static int parseInt(Object value, int def) {
        if (value == null) {
            return def;
        }

        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        try {
            return Integer.parseInt(String.valueOf(value));
        } catch (Exception e) {
            return def;
        }
    }

    public static int parseInt(Object s, int def, Integer min, Integer max) {
        int i = parseInt(s, def);
        if (min != null && i < min) {
            return min;
        }
        if (max != null && i > max) {
            return max;
        }

        return i;
    }

    public static int parseInt(Object s) {
        return parseInt(s, 0);
    }

    /*
     * sleep返回编译时异常
     * */
    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static String cutEndsWith(String string, String cutString) {
        while (string.endsWith(cutString)) {
            string = string.substring(0, string.length() - 1);
        }
        return string;
    }

    /**
     * 数据表字段名转换为驼峰式名字的实体类属性名
     *
     * @param tabAttr 数据表字段名
     * @return 转换后的驼峰式命名
     */
    public static String camelize(String tabAttr, String prefix) {
        if (Verification.checkNull(tabAttr)) return tabAttr;
        String regex = "(.*)" + prefix + "(\\w)(.*)";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(tabAttr);
        if (matcher.find()) {
            return camelize(matcher.group(1) + matcher.group(2).toUpperCase() + matcher.group(3), prefix);
        } else {
            return tabAttr;
        }
    }

    /**
     * 驼峰式的实体类属性名转换为数据表字段名
     *
     * @param camelCaseStr 驼峰式的实体类属性名
     * @return 转换后的以"_"分隔的数据表字段名
     */
    public static String decamelize(String camelCaseStr, String prefix) {
        return Verification.checkNull(camelCaseStr) ? camelCaseStr : camelCaseStr.replaceAll("[A-Z]", prefix + "$0").toLowerCase();
    }

    public static String camelize(String tabAttr) {
        return camelize(tabAttr, "-");
    }

    public static String decamelize(String camelCaseStr) {
        return decamelize(camelCaseStr, "-");
    }

    public static Map<String, Object> camelizeMap(Map<?, ?> map, String prefix) {
        Map<String, Object> rMap = new HashMap<>();
        for (Object key : map.keySet()) {
            rMap.put(camelize(key.toString(), prefix), map.get(key));
        }
        return rMap;
    }

    public static Map<String, Object> camelizeMap(Map<?, ?> map) {
        return camelizeMap(map, "-");
    }

    public static List<String> patternGets(String string, String regex, Integer groupIndex, int flg) {
        Matcher matcher = Pattern.compile(regex, flg).matcher(string);
        List<String> result = new Arraylist<>();
        while (matcher.find()) {
            if (groupIndex != null) {
                result.add(matcher.group(groupIndex));
            } else {
                result.add(matcher.group());
            }
        }
        return result;
    }

    public static List<String> patternGets(String string, String regex) {
        return patternGets(string, regex, null, 0);
    }

    public static boolean patternExist(String string, String regex) {
        return Verification.checkNotNull(Transformation.patternGet(string, regex));
    }

    public static String patternGet(String string, String regex) {
        return lambdaTryCatch(() -> requireNonNullElseGet(patternGets(string, regex).get(0), emptyStringSupplier), emptyStringSupplier);
    }

    public static String patternGet(String string, String regex, Integer groupIndex, int flg) {
        return lambdaTryCatch(() -> requireNonNullElseGet(patternGets(string, regex, groupIndex, flg).get(0), emptyStringSupplier), emptyStringSupplier);
    }

    public static String patternGet(String string, String regex, Integer groupIndex) {
        return lambdaTryCatch(() -> requireNonNullElseGet(patternGets(string, regex, groupIndex, 0).get(0), emptyStringSupplier), emptyStringSupplier);
    }

    private static class Arraylist<E> extends ArrayList<E> {
        @Override
        public E get(int index) {
            try {
                return super.get(index);
            } catch (IndexOutOfBoundsException exception) {
                return null;
            }
        }
    }

    public static String replaceEach(String text, String[] searchList, String replacementList) {
        for (String search : searchList) {
            text = StringUtils.replace(text, search, replacementList);
        }
        return text;
    }

    public static List<String> checkUnParsedVariable(String sql) {
        Pattern p = Pattern.compile("([$|#]\\{)(\\w+)(})");
        Matcher m = p.matcher(sql);
        List<String> unparsed = new ArrayList<>();
        while (m.find()) {
            unparsed.add(replaceEach(m.group(), new String[]{"$", "#", "{", "}"}, ""));
        }
        return unparsed;
    }

    public static boolean equals(Object t, Object t2) {
        if ((t instanceof CharSequence || t instanceof Number) && (t2 instanceof CharSequence || t2 instanceof Number)) {
            return String.valueOf(t).equals(String.valueOf(t2));
        }

        throw new IllegalArgumentException(t.getClass() + "," + t2.getClass() + " compare Unsupported");
    }

    public static float round(Number n, int length) {
        try {
            NumberFormat nf = NumberFormat.getNumberInstance();
            nf.setMaximumFractionDigits(length);
            nf.setRoundingMode(RoundingMode.HALF_UP);
            return Float.parseFloat(nf.format(n));
        } catch (NumberFormatException exception) {
            return 0;
        }
    }

    public static float round(Number n) {
        return round(n, 2);
    }

    public static Number max(List<? extends Number> arr) {
        Number max = 0;
        for (Number n : arr) {
            if (n.doubleValue() > max.doubleValue()) {
                max = n;
            }
        }
        return max;
    }

    public static Number max(Number... n) {
        return max(ArrayUtils.asList(n));
    }

    public static Number min(List<? extends Number> arr) {
        Number min = Integer.MAX_VALUE;
        for (Number n : arr) {
            if (n.doubleValue() < min.doubleValue()) {
                min = n;
            }
        }
        return min;
    }

    public static Number min(Number... n) {
        return min(ArrayUtils.asList(n));
    }

    public static boolean instanceInt(Object obj) {
        return obj instanceof Integer;
    }

    public static String encodeBase64(String v) {
        return encoder.encodeToString(v.getBytes(StandardCharsets.UTF_8));
    }

    public static String decodeBase64(String v) {
        return new String(decoder.decode(v), StandardCharsets.UTF_8);
    }

    public static Object lambdaIf(Object... stateValueArray) {
        int size = stateValueArray.length;
        Object defaultValue = "";
        if ((size & 1) == 1) {
            defaultValue = stateValueArray[stateValueArray.length - 1];
            size--;
        }

        int keyIndex = 0;
        int valueIndex = 1;
        while (size >= valueIndex) {
            if (Transformation.castToBoolean(stateValueArray[keyIndex])) return stateValueArray[valueIndex];
            keyIndex += 2;
            valueIndex += 2;
        }
        return defaultValue;
    }

    @FunctionalInterface
    public interface SupplierVoid {
        void get();
    }

    @FunctionalInterface
    public interface SupplierVoidExceptionArgument {
        void get(Throwable throwable);
    }

    @FunctionalInterface
    public interface SupplierVoidWithException {
        void get() throws Throwable;
    }

    @FunctionalInterface
    public interface SupplierWithException<T> {
        T get() throws Throwable;
    }

    public static <T> T lambdaTryCatch(SupplierWithException<T> tryPath, Supplier<T> catchPath, SupplierVoid finallyPath) {
        try {
            return tryPath.get();
        } catch (Throwable exception) {
            return catchPath.get();
        } finally {
            finallyPath.get();
        }
    }

    public static <T> T lambdaTryCatch(SupplierWithException<T> tryPath, Supplier<T> catchPath) {
        try {
            return tryPath.get();
        } catch (Throwable exception) {
            return catchPath.get();
        }
    }

    public static <T> T lambdaTryCatch(SupplierWithException<T> tryPath, Supplier<T> catchPath, SupplierVoidExceptionArgument callback) {
        try {
            return tryPath.get();
        } catch (Throwable exception) {
            callback.get(exception);
            return catchPath.get();
        }
    }

    public static void lambdaTryCatch(SupplierVoidWithException tryPath) {
        try {
            tryPath.get();
        } catch (Throwable ignored) {

        }
    }

    public static void lambdaTryCatch(SupplierVoidWithException tryPath, SupplierVoidExceptionArgument catchPath) {
        try {
            tryPath.get();
        } catch (Throwable throwable) {
            catchPath.get(throwable);
        }
    }

    public static boolean checkLatitudeAndLongitude(Object lat, Object lon) {
        return checkLatitudeAndLongitude(lat + "," + lon);
    }

    public static boolean checkLatitudeAndLongitude(String coordinate) {
        // 正则表达式匹配纬度,经度格式（允许空格）
        String regex = "^-?\\d+(\\.\\d+)?,\\s*-?\\d+(\\.\\d+)?$";
        if (!Pattern.matches(regex, coordinate)) {
            return false; // 格式不匹配
        }

        // 分割纬度和经度
        String[] parts = coordinate.split(",");
        double lat = Double.parseDouble(parts[0].trim());
        double lon = Double.parseDouble(parts[1].trim());

        // 验证范围
        return (lat >= -90 && lat <= 90) && (lon >= -180 && lon <= 180);
    }

    public static String getStackTrace(final Throwable throwable) {
        if (throwable == null) {
            return "";
        }
        final StringWriter sw = new StringWriter();
        throwable.printStackTrace(new PrintWriter(sw, true));
        return sw.toString();
    }

    /**
     * 返回不为null且不为空的值
     */
    @SafeVarargs
    public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T>... supplier) {
        if (Verification.checkNotNull(obj)) return obj;
        for (Supplier<? extends T> nonNullSupplier : ArrayUtils.asList(supplier).stream().filter(Objects::nonNull).collect(Collectors.toList())) {
            T t = nonNullSupplier.get();
            if (Verification.checkNotNull(t)) return t;
        }
        throw new NullPointerException("Supplier returned null");
    }

    /**
     * 返回不为null的值
     */
    @SafeVarargs
    public static <T> T requireNotNullElseGet(T obj, Supplier<? extends T>... supplier) {
        if (obj != null) return obj;
        for (Supplier<? extends T> nonNullSupplier : ArrayUtils.asList(supplier).stream().filter(Objects::nonNull).collect(Collectors.toList())) {
            T t = nonNullSupplier.get();
            if (t != null) return t;
        }
        throw new NullPointerException("Supplier returned null");
    }

    public static <T> T mapKeyToLowerCase(T obj) {
        return Transformation.lambdaTryCatch(() -> processObject(obj), () -> obj);
    }

    private static <T> T processObject(T obj) throws IllegalAccessException {
        if (obj == null || isSimpleType(obj.getClass())) {
            return obj;
        }

        if (obj instanceof Map || obj instanceof Collection) {
            processMapList(obj);
            return obj;
        }

        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            Object fieldValue = field.get(obj);
            processMapList(fieldValue);
        }
        return obj;
    }

    // 判断是否是简单类型（不需要递归处理的类型）
    private static boolean isSimpleType(Class<?> clazz) {
        return clazz.isPrimitive() || CharSequence.class.isAssignableFrom(clazz) || clazz.equals(Boolean.class) || clazz.equals(Character.class) || clazz.equals(Void.class) || Date.class.isAssignableFrom(clazz) ||
                clazz.equals(LocalDate.class) || clazz.equals(LocalDateTime.class) || clazz.equals(LocalTime.class) || Number.class.isAssignableFrom(clazz) || clazz.isArray() || clazz.isAnnotation() || clazz.isEnum();
    }

    @SuppressWarnings("unchecked")
    private static void processMapList(Object object) throws IllegalAccessException {
        if (object instanceof Map) {
            // 处理 Map 类型的字段
            processMap((Map<Object, Object>) object);
        } else if (object instanceof Collection<?>) {
            ((Collection<?>) object).forEach(e -> {
                if (e instanceof Map) Transformation.lambdaTryCatch(() -> processMap((Map<Object, Object>) e));
            });
        } else {
            // 对于非Map字段，检查是否需要递归处理
            processObject(object);
        }
    }

    @SuppressWarnings("unchecked")
    private static void processMap(Map<Object, Object> map) throws IllegalAccessException {
        if (map == null || map.isEmpty()) {
            return;
        }

        // 创建一个新Map来存储处理后的条目
        Map<Object, Object> newMap = new HashMap<>();

        for (Map.Entry<?, ?> entry : map.entrySet()) {
            Object key = entry.getKey();
            Object value = entry.getValue();

            // 处理key：转为小写
            Object newKey = key instanceof String && !isLowerCamelCase((String) key) ? ((String) key).toLowerCase() : key;

            // 处理value
            Object newValue;
            if (value instanceof Map) {
                // 如果value是Map，递归处理
                newValue = new HashMap<>();
                ((Map<Object, Object>) newValue).putAll((Map<?, ?>) value);
                processMap((Map<Object, Object>) newValue);
            } else {
                // 应用transfer方法
                newValue = transferMapValue(value);
                // 检查value是否是复杂对象需要递归处理
                if (newValue != null && !isSimpleType(newValue.getClass())) {
                    processObject(newValue);
                }
            }

            newMap.put(newKey, newValue);
        }

        // 清空原Map并放入处理后的条目
        map.clear();
        map.putAll(newMap);
    }

    public static boolean isLowerCamelCase(String key) {
        if (key == null || key.isEmpty()) {
            return false;
        }
        // 正则表达式解释：
        // ^[a-z] - 以小写字母开头
        // [a-zA-Z0-9]* - 后面跟着零个或多个字母或数字
        // (([A-Z][a-zA-Z0-9]+)+)? - 可选的一个或多个大写字母开头的单词
        return key.matches("^[a-z][a-zA-Z0-9]*(([A-Z][a-zA-Z0-9]+)+)?$");
    }

    private static Object transferMapValue(Object value) {
        if (value == null) {
            return null;
        }

        if (value instanceof Date) {
            return formatDate(((Date) value));
        } else if (value instanceof LocalDateTime) {
            return formatDate(((LocalDateTime) value));
        } else if (hasTimestampClass && value instanceof TIMESTAMP) {
            return formatDate(((TIMESTAMP) value));
        }
        // 这里只是示例，实际应根据需求实现
        return value;
    }

    // 线程安全的格式化器
    private static final DateTimeFormatter DATE_TIME_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    // 格式化Date对象
    public static String formatDate(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).format(DATE_TIME_FORMATTER);
    }

    // 格式化LocalDateTime对象
    public static String formatDate(LocalDateTime dateTime) {
        return dateTime.format(DATE_TIME_FORMATTER);
    }

    // 格式化LocalDateTime对象
    public static String formatDate(TIMESTAMP dateTime) {
        String date = lambdaTryCatch(() -> formatDate(dateTime.toLocalDateTime()), () -> StringUtils.toString(dateTime));
        return date.replace(patternGet(date, "\\.(.*?)$"), "");
    }
}
